package cc.idoone.download.statistics;

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Enumeration;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.museframework.util.WebUtils;

/**
 * <pre>
 * 下载文件
 * -/download/path/..: 文件存放路径
 * 或
 * -/download/fileId: 文件标识
 * -fileName: 下载文件名称（可选）
 * </pre>
 * 
 * @author hemj
 * @date 2017-03-27
 */
public class DownloadServlet extends HttpServlet {
	private static final long serialVersionUID = 5440638387516373848L;
	/** 记录日志 */
	private static Logger logger = LoggerFactory.getLogger(DownloadServlet.class);

	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
			IOException {
		doPost(req, resp);
	}

	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		BufferedInputStream in = null;
		OutputStream out = null;
		String path = "D:\\360Downloads\\WS_APP_PAY_SDK_BASE_2.0.zip"; // 通过路径下载
		int totalLen = 0, totalSend = 0;
		int rangeOffset = 0, rangeEnd = 0, rangeLen = 0;
		boolean partial = false;
		try {
			// 请求信息
			logger.info("客户端IP: {}", WebUtils.getClientIpAddr(req));
			logger.info("请求链接: {}", req.getRequestURI());
			logger.info("发生页面: {}", req.getHeader("referer"));
			logger.info("语言: {}", req.getHeader("accept-language"));
			logger.info("设备类型: {}", req.getHeader("user-agent"));
			logger.info("操作系统: {}", req.getHeader("user-agent"));
			logger.info("浏览器:: {}", req.getHeader("user-agent"));
			logger.info("请求范围: {}", req.getHeader("range"));

			logger.info("req.getRequestURL(): {}: ", req.getRequestURL());
			logger.info("req.getServletPath(): {}: ", req.getServletPath());
			logger.info("req.getServerName(): {}: ", req.getServerName());

			// 文件流输出
			in = new BufferedInputStream(new FileInputStream(path));
			String fileName = "下载测试.zip";
			String extension = fileName.substring(fileName.lastIndexOf('.') + 1);
			String contentType = CommUtils.getContentType(extension);
			rangeLen = totalLen = in.available();

			// 请求范围
			String _range = req.getHeader("range");
			if (_range != null) {
				_range = _range.substring(6);
				String[] _ranges = _range.split("-");
				if (_ranges.length < 1 || _ranges[0].isEmpty()) {
					rangeOffset = 0;
				} else {
					rangeOffset = Integer.parseInt(_ranges[0]);
				}
				if (_ranges.length < 2 || _ranges[1].isEmpty()) {
					rangeEnd = totalLen - 1;
					rangeLen = totalLen - rangeOffset;
				} else {
					rangeEnd = Integer.parseInt(_ranges[1]);
					rangeLen = rangeEnd - rangeOffset + 1;
				}
				partial = true;
			}

			// 设置响应头信息
			resp.setHeader("Accept-Ranges", "bytes");
			resp.setHeader("Cache-Control", "no-cache");
			resp.setContentType(contentType + ";charset=UTF-8");
			resp.setContentLength(rangeLen);
			fileName = URLEncoder.encode(fileName, "UTF8").replaceAll("\\+", "%20");
			resp.setHeader("Content-disposition", "attachment; filename=" + fileName);
			if (partial) {
				// 分段下载
				resp.setHeader("Content-Range",
						String.format("bytes %d-%d/%d", rangeOffset, rangeEnd, rangeEnd + 1));
				resp.setStatus(206);
				// 跳过已下载的长度
				in.skip(rangeOffset);
			}

			out = resp.getOutputStream();
			byte[] cache = new byte[4096];
			int read = 0;

			if (partial) {
				while (totalSend < rangeLen && (read = in.read(cache)) > 0) {
					totalSend += read;
					if (totalSend >= rangeLen) {
						read = rangeLen - totalSend + read;
					}
					out.write(cache, 0, read);
					Thread.sleep(20);
				}
			} else {
				while ((read = in.read(cache)) > 0) {
					totalSend += read;
					out.write(cache, 0, read);
					Thread.sleep(20);
				}
			}

			if (rangeOffset + totalSend >= totalLen) {
				logger.info("[ Download Finish !! ]");
			}

			out.flush();
		} catch (java.io.FileNotFoundException e) {
			logger.error("FileNotFound: {}", path);
		} catch (Exception e) {
			logger.warn("request_url: {}", req.getRequestURL());
			logger.warn("request_headers: {}", printRequestHeaders(req));
			logger.info("File Size: {}/{}", totalSend, totalLen);
			logger.error("Exception: " + e.getMessage());
		} finally {
			IOUtils.closeQuietly(in);
			IOUtils.closeQuietly(out);
		}
	}

	// 迅雷多线程下载
	// cache-control: no-cache
	// connection: keep-alive
	// host: 192.168.1.72 !!
	// range: bytes=36699677-36961820 !!
	// user-agent: Mozilla/4.0 !!

	// 浏览器下载
	// host: 192.168.1.72:8080 !!
	// connection: keep-alive
	// upgrade-insecure-requests: 1
	// user-agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 !!
	// (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
	// accept:
	// text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
	// referer: http://192.168.1.72/smp-app/test/getui-login.html !!
	// accept-encoding: gzip, deflate, sdch
	// accept-language: zh-CN,zh;q=0.8 !!
	// cookie:

	/** 打印输出所有的请求消息头(比较重要的有：host,user-agent,referer,accept-language) */
	private String printRequestHeaders(HttpServletRequest req) {
		Enumeration<String> e = req.getHeaderNames();
		StringBuffer sb = new StringBuffer();
		while (e.hasMoreElements()) {
			String headerName = e.nextElement();
			Enumeration<String> headerValues = req.getHeaders(headerName);
			while (headerValues.hasMoreElements()) {
				sb.append(headerName).append(": ").append(headerValues.nextElement()).append("\n");
			}
		}
		return sb.toString();
	}
}
